using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Taxhui.Utils.Network.Delegate;
using Taxhui.Utils.Network.Tcp.Awaitable;
using Taxhui.Utils.Network.Tcp.NetSession;
using Taxhui.Utils.Network.Tcp.TcpConfig;
using System.Threading.Tasks;
using Taxhui.Utils.Network.CustomProc;

namespace Taxhui.Utils.Network.Tcp
{
    public class ClientAgent : NetEngineBased, IDisposable
    {
        private bool _isruning = true;
        private SessionType _sessionType = SessionType.Packet;

        #region 构造函数

        internal ClientAgent(
            SessionType sessionType,
            ClientConfig cfg,
            NotifyEventHandler<CompleteNotify, Session> completetionNotify,
            ICustomDataProc hdProc)
            : base(sessionType, cfg, completetionNotify, hdProc)
        {
            _sessionType = sessionType;
            //断线检测线程
            Task.Run(() => CheckAlive());
        }

        #endregion 构造函数

        #region 断线检测线程

        private void CheckAlive()
        {
            byte[] emptyHeart = new byte[] { 0, 0, 0, 0 };
            while (_isruning)
            {
                //遍历所有Session
                for (int i = 0; i < _sessions.Count; i++)
                {
                    var session = (PackSessionBased)_sessions[i];
                    if (session.State == ConnectionState.Closed ||
                    session.State == ConnectionState.None)
                    {
                        _logger.WriteLog("心跳检测线程 状态= Closed || None");
                        _sessions.RemoveAt(i); i--; //从列表里删除无效的Session
                        continue;
                    }
                    //正在连接的Session不发送心跳包
                    if (session.State == ConnectionState.Connecting)
                        continue;
                    if (_cfg.CheckDisconnect)
                    {
                        //超时
                        if ((int)(DateTime.Now - session.HeartTime).TotalSeconds > _cfg.CheckDisconnectTimeout)
                        {
                            _logger.WriteLog("心跳线程超时 ! state：" + session.State.ToString() + " present_time：" + DateTime.Now.ToString() + " heart_time：" + session.HeartTime.ToString());
                            session.Close(true);
                            _sessions.RemoveAt(i); i--;
                            continue;
                        }
                    }
                    //开启了掉线检测
                    if (_cfg.AutoHeartBeat && _sessionType == SessionType.Packet)
                    {
                        //如果数据正在发送，就不发送心跳 isuchannel=0时表示未被占用
                        if (session._isuchannel == 0)
                        {
                            var awaiter = _awaiterPool.Take();
                            //4个字节空包头
                            awaiter.Args.SetBuffer(emptyHeart, 0, emptyHeart.Length);
                            //发送了就完事，不处理
                            NetHelper.SendAsync(session.Socket, awaiter, (a, e) => _awaiterPool.Return(a));
                        }
                    }
                }
                Thread.Sleep(5000);
            }
        }

        #endregion 断线检测线程

        #region 连接服务器

        public void ConnectToServer(string serverAddr, int port)
        {
            if (string.IsNullOrEmpty(serverAddr))
                throw new ArgumentNullException("服务器地址不能为空");
            if (port <= 0 || port > 65535)
                throw new ArgumentOutOfRangeException("Port", port, "端口超出范围1~65535");
            IPAddress ipAddr = null;
            var ips = Dns.GetHostAddresses(serverAddr);
            if (ips != null)
            {
                if (ips.Length > 0)
                {
                    IPAddress.TryParse(ips[0].ToString(), out ipAddr);
                }
            }
            if (ipAddr is null)
                throw new ArgumentException("服务器地址无法解析！");
            IPEndPoint ep = new IPEndPoint(ipAddr, port);
            ConnectToServer(ep);
        }

        public void ConnectToServer(IPEndPoint ep)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            var awaiter = _awaiterPool.Take();
            var session = _sessionPool.Take();
            //设置正在连接状态
            session.State = ConnectionState.Connecting;

            awaiter.Args.RemoteEndPoint = ep;

            NetHelper.ConnectAsync(socket, awaiter, (a, e) =>
            {
                _awaiterPool.Return(a); //返还对象
                if (e != SocketError.Success)
                {
                    _logger.WriteLog("连接失败,错误:" + e.ToString());
                    //触发通知事件
                    _completetionNotify?.Invoke(CompleteNotify.OnClosed, session);
                    //返还session到池
                    _sessionPool.Return(session);
                    return;
                }
                //连接成功后将session放到列表中
                _sessions.Add(session);
                session.Attach(socket);
                //触发通知事件
                _completetionNotify?.Invoke(CompleteNotify.OnConnected, session);
                //开始接收数据
                session.StartProcess();
            });
        }

        #endregion 连接服务器

        #region 发送广播包

        public override void Broadcast(byte[] data)
        {
            Broadcast(data, 0, data.Length);
        }

        public override void Broadcast(byte[] data, int offset, int length)
        {
            if (_sessionType == SessionType.Full)
            {
                foreach (FullSessionBased session in _sessions)
                {
                    session.SendAsync(data, offset, length);
                }
            }
            else
            {
                foreach (PackSessionBased session in _sessions)
                {
                    session.SendAsync(data, offset, length);
                }
            }
        }

        #endregion 发送广播包

        #region 断开所有连接

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="notify">是否触发通知</param>
        public override void ClearConnections(bool notify)
        {
            foreach (Session session in _sessions)
                session.Close(notify);

            _sessions.Clear();
        }

        #endregion 断开所有连接

        #region 析构函数

        public override void Dispose()
        {
            if (_isruning)
            {
                _isruning = false;
                _logger.Dispose();
                ClearConnections(false);
                _awaiterPool.Dispose();
                _sessionPool.Dispose();
            }
        }

        #endregion 析构函数
    }
}